#pragma once
#include <atomic>

namespace galois::data {

template <class T>
class LockFreeStack {
public:
  struct Node {
    T value;
    Node *next;
    Node() : next(nullptr) {
    }
  };

private:
  std::atomic<Node *> head{nullptr};

public:
  LockFreeStack() : head(nullptr) {};
  void push(Node *nd) {
    nd->next = head.load(std::memory_order_relaxed);
    while (!head.compare_exchange_weak(nd->next, nd, std::memory_order_release, std::memory_order_relaxed));
  }
  Node *pop() {
    Node *od = head.load(std::memory_order_relaxed);
    while (od && !head.compare_exchange_weak(od, od->next, std::memory_order_release, std::memory_order_relaxed));
    return od;
  }
};

#include <iostream>
#include <vector>
#include <type_traits>
#include <functional>
/*
 数据层
 
 数据大类型分两类
 1.带缓存的数据:    复杂数据推荐使用，完全替代智能指针【生产者】rule_data + 【消费者】span . framework管理数据。 
 2.raw_data:       基础数据推荐使用  自定义原始数据 T

 数据的使用者有三l类：
 1.数据的生产者: 用于生产数据。                  span: data的一份视图
 2.数据的消费者: 用于消费数据。                  data：
 3.framework:   用于管理数据并触发rule计算。     data & span

 数据驱动方式
 1. update 上游数据变化驱动下游.
 2. flush 以field为单位刷新rule.

 数据异步与互斥
 update:
 1.rule_data + span支持异步.
 2.raw_data的条件是  !(raw_data.update_on_run && rule.trigger_with_others && raw_data非原子【非 int float bool 等基础类型】)
 3.如果条件2不满足 调度系统会兜底 .
 flush:
 1. flush会并发的刷新所有节点一遍，framework掌控节奏不存在互斥问题。
 2. 刷新的机制.
 
 数据特性，以宏的方式提供
 1. RULE_ARG(T)：    为数据增加一个类型为T（任意可浅拷贝struct）的参数。
 2. RULE_DATA(T):    只想让消费者看到它可用此特性，否则消费者全可见。
 3. RULE_ASYNC()：   当rule希望并行计算时可使用此特性.
 5. RULE_TIMEOUT(N): 当rule希望管控其执行时间并报警时使用此特性，framework将会监控其 run sleep block wait 时间。
*/
namespace rule
{
    /**
     * @brief 消费者数据视图： 使用者包括 framework & rule函数插件。framework保证span所有操作单线程。
     * @tparam T 数据类型
     * @tparam N 消费者关心的数据大小，如果为0表示消费者关心全部数据。否则只关心N个数据。framework在触发rule计算时会调用feed函数。
     * @tparam TRIGGER span是否触发rule计算。
     */
    template <typename T, int N = 1, bool TRIGGER = true>
    class span
    {
    public:
        span(size):data_size_(size){}
        constexpr bool trigger() const { return TRIGGER; }
        uint64_t start() const { return start_; }
        int size() const
        {
            return end_ - start_;
        }
        /**
         * @brief framework 在触发计算前调用
         * @param M span 对应 data 缓存大小
         * @param end data 缓存
         */
        void feed()
        {
            end_ = target;
            if (size() > N)
            {
                move(size() - N,data_size_);
            }
        }
        void update(uint64_t t){target = t;}
        void release(int n){
            start_ += n;
            data_  += n;
        }
        const T* operator[](size_t index) const{
            return data_[index];
        }
        const T* at(size_t index) const{
            if (index >= size()) {
                throw std::out_of_range("Index out of range");
            }
            return data_[index];
        }
    private:
        void move(uint64_t n, int data_size)
        {
            auto real_start0 = start_ % data_size;
            start_ += n;
            auto real_start1 = start_ % data_size;
            data_ += real_start1 - real_start0;
        }
        T **data_;
        uint64_t target = 0; 
        int data_size_ = 0;
        uint64_t start_ = 0;
        uint64_t end_ = 0;
    };


    template <typename, typename = std::void_t<>>
    struct has_clear : std::false_type {};

    template <typename T>
    struct has_clear<T, std::void_t<decltype(std::declval<T>().Clear())>> : std::true_type {};

    template <typename T>
    constexpr bool has_clear_v = has_clear<T>::value;
    
    /**

     */
    template <typename T, int N>
    class rule_data
    {
    public:
        void push(std::function<void(T* val)>& fill){
            T* data = nullptr;
            if constexpr(has_clear_v<T>){
                if(recycle_.size() > 0){
                    data = recycle_.back();
                    data->Clear();
                    recycle_.pop_back();
                }else{
                    data = new T();
                }
            }else{
                data = new T();
            }
            fill(data);
            data_[end_] = data;
            data_[end_ + N] = data;
            ++end_;
        }
        void recycle(T* val){
            if constexpr(has_clear_v<T>){
                recycle_.push_back(val);
            }else{
                delete val;
            }
        }
    private:
        uint64_t end_ = 0;
        T *data_[N * 2];
        std::vector<T*> recycle_;
    };

    template <typename T>
    struct RUD;
    
    template <typename T, int N>
    struct RUD<rule_data<T, N>>
    {
        using value_type = T;
        static constexpr int size = N;
    };

}


} // namespace galois::data